﻿using System;
using System.Collections.Generic;
using System.Web.UI;
using System.Xml.Linq;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Publishing;

namespace Eneta.Portal.Common
{
    public static class SPExtensions
    {       
        public static Control FindControl(this Control control, string name, bool recursive)
        {
            if (!recursive)
                return control.FindControl(name);

            foreach (Control child in control.Controls)
            {
                if (child.ID == name)
                    return child;
                var ctl = child.FindControl(name, recursive);
                if (ctl != null)
                    return ctl;
            }
            return null;
        }

        public static bool HasWeb(this SPWeb web, string webName, out string webId)
        {
            foreach (SPWeb currentWeb in web.Webs)
            {
                if (currentWeb.Title == webName)
                {
                    webId = currentWeb.ID.ToString();
                    return true;
                }
            }
            webId = string.Empty;
            return false;
        }
        
        public static bool HasList(this SPWeb web, string listName)
        {
            foreach(SPList list in web.Lists)
                if (list.Title == listName)
                    return true;
            return false;
        }

        public static bool HasField(this SPList list, string fieldName)
        {
            foreach (SPField field in list.Fields)
            {
                if (field.InternalName == fieldName)
                {
                    return true;
                }
            }
            return false;
        }

        public static bool HasField(this SPWeb web, string fieldName)
        {
            foreach (SPField field in web.Fields)
            {
                if (field.InternalName == fieldName)
                {
                    return true;
                }
            }
            return false;
        }

        public static void AddFieldsToContentType(this SPWeb web, string listName, string contentTypeName)
        {
            if (web == null)
                return;
            var list = web.Lists[listName];
            if (list == null)
                return;
            var cType = list.ContentTypes[contentTypeName];
            if (cType == null)
                return;
            foreach (var link in list.GetFieldLinks())
            {
                if (!cType.Fields.ContainsField(link.Name))
                    cType.FieldLinks.Add(link);
            }
            cType.Update();
        }

        public static List<SPFieldLink> GetFieldLinks(this SPList list)
        {
            var fields = new List<SPFieldLink>();
            foreach (SPField field in list.Fields)
            {
                if (!field.Hidden && !field.ReadOnlyField)
                {
                    fields.Add(new SPFieldLink(field));
                }
            }
            return fields; 
        }

        /// <summary>
        /// This is incase of site columns. There is no target list.
        /// </summary>
        /// <param name="web"></param>
        /// <param name="sourceListName"></param>
        /// <param name="fieldInternalName"></param>
        public static void EnsureLookupField(this SPWeb web, string sourceListName, string fieldInternalName)
        {
            if (web == null)
                return;
            if (!web.Fields.ContainsField(fieldInternalName))
                return;
            SPFieldLookup field = web.Fields.GetFieldByInternalName(fieldInternalName) as SPFieldLookup;
            
            if (field == null)
                return;
            
            if (!web.HasList(sourceListName))
                return;

            Console.WriteLine("Before LookupField {0} SchemaXML: {1}", fieldInternalName, field.SchemaXml);
            XElement fieldSchemaXml = XElement.Parse(field.SchemaXml);
            //Deleting the invalid field and adding new one. Cannot change the list or update schema - field is invalid still.
            if (!field.CanBeDeleted)
                return;
            if (field.CanBeDeleted)
            {
                field.Delete();
            }

            var listAttribute = fieldSchemaXml.Attribute(XName.Get("List"));
            listAttribute.Value = web.Lists[sourceListName].ID.ToString("B");

            web.Fields.AddFieldAsXml(fieldSchemaXml.ToString());

            field = web.Fields.GetFieldByInternalName(fieldInternalName) as SPFieldLookup;

            field.LookupWebId = web.ID;
            field.AllowMultipleValues = true;
            field.Update();

            Console.WriteLine("After LookupField {0} SchemaXML: {1}", fieldInternalName, field.SchemaXml);
        }

        public static void EnsureLookupField(this SPWeb web, string targetListName, string sourceListName, string fieldInternalName)
        {
            if (web == null)
                return;
            if (!web.HasList(targetListName))
                return;
            SPList list = web.Lists[targetListName];
            if (!list.HasField(fieldInternalName))
                return;
            SPFieldLookup field = list.Fields.GetFieldByInternalName(fieldInternalName) as SPFieldLookup;
            if (field == null)
                return;
            if (!web.HasList(sourceListName))
                return;
            field.LookupList = web.Lists[sourceListName].ID.ToString();
            field.Update();
        }

        public static void AddFieldToContentType(this SPWeb web, Guid fieldId, SPContentTypeId contentTypeId)
        {
            var contentType = web.ContentTypes[contentTypeId];
            if (contentType == null)
                return;
            var field = web.Fields[fieldId];
            if (field == null)
                return;

            if (!contentType.Fields.ContainsField(field.InternalName))
            {
                contentType.FieldLinks.Add(new SPFieldLink(field));
                contentType.Update();
            }
        }

        public static bool IsSet(this object o)
        {
            return (o != null);
        }

        public static string GetFieldString(this SPListItem item, Guid fieldId)
        {
            var value = item[fieldId];
            if (value == null)
                return string.Empty;

            return value.ToString();
        }

        public static string GetFieldString(this SPListItem item, string fieldName)
        {
            var value = item.ValueOf(fieldName);
            if (value == null)
                return string.Empty;

            return value.ToString();
        }

        public static bool GetFieldBool(this SPListItem item, Guid fieldId)
        {
            var value = item[fieldId];
            if (value == null)
                return false;

            return (bool)value;
        }

        public static bool GetFieldBool(this SPListItem item, string fieldName)
        {
            var value = item.ValueOf(fieldName);
            if (value == null)
                return false;

            return (bool)value;
        }

        // SP2010 has bugs with finding fields by internal name from fields collection.
        // Still asking field by field title (may be customized) works. 
        public static object ValueOf(this SPListItem item, string name)
        {
            if(item == null)
                throw new ArgumentException("Item cannot be null", "item");

            var fieldId = Guid.Empty;

            foreach (SPField field in item.ParentList.Fields)
                if (field.InternalName.ToLower() == name.ToLower())
                    fieldId = field.Id;
                else if (field.Title.ToLower() == name.ToLower())
                    fieldId = field.Id;

            if (fieldId == Guid.Empty)
            {
                var message = "Cannot find field with name {0} from list {1}";
                message = string.Format(message, name, item.ParentList.Title);
                throw new ArgumentException(message, "name");
            }

            return item[fieldId];
        }

        public static void SetValue(this SPListItem item, string name, object value)
        {
            if (item == null)
                throw new ArgumentException("Item cannot be null", "item");

            var fieldId = Guid.Empty;

            foreach (SPField field in item.ParentList.Fields)
                if (field.InternalName.ToLower() == name.ToLower())
                    fieldId = field.Id;
                else if (field.Title.ToLower() == name.ToLower())
                    fieldId = field.Id;

            if (fieldId == Guid.Empty)
            {
                var message = "Cannot find field with name {0} from list {1}";
                message = string.Format(message, name, item.ParentList.Title);
                throw new ArgumentException(message, "name");
            }

            item[fieldId] = value;
        }

        public static Guid FieldIdOf(this SPListItem item, string name)
        {
            if (item == null)
                throw new ArgumentException("Item cannot be null", "item");

            var fieldId = Guid.Empty;

            foreach (SPField field in item.ParentList.Fields)
                if (field.InternalName.ToLower() == name.ToLower())
                    fieldId = field.Id;
                else if (field.Title.ToLower() == name.ToLower())
                    fieldId = field.Id;

            if (fieldId == Guid.Empty)
            {
                var message = "Cannot find field with name {0} from list {1}";
                message = string.Format(message, name, item.ParentList.Title);
                throw new ArgumentException(message, "name");
            }

            return fieldId;
        }
    }
}
